﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Esponce.Common;
using Esponce.QRCode;
using Microsoft.Win32;
using System.Windows;

namespace Esponce.Models
{
	public class GenerateViewModel : INotifyPropertyChanged
	{
		#region Constructor
		public GenerateViewModel()
		{
			//Default values
			this.Content = "your content goes here";
			this.ColorList = new ObservableCollection<ColorListItem>()
			{
				new ColorListItem() { Name = "Default", IsDefault = true },
				new ColorListItem(Colors.Transparent),
				new ColorListItem(Colors.Black),
				new ColorListItem(Colors.White),
				new ColorListItem(Colors.Red),
				new ColorListItem(Colors.Green),
				new ColorListItem(Colors.Blue),
			};

#if DEBUG
			if (Settings.IsDesignMode)
			{
				this.Content = "hello world";
				this.Format = "png";
				this.SizeIndex = 10;
				this.EncodeModeIndex = 4;
				this.EncodeModeIndex = 3;
				this.ErrorCorrectionIndex = 0;
			}
#endif
		}
		#endregion

		#region Public properties
		public string Content
		{
			get
			{
				return this.content;
			}
			set
			{
				if (this.content != value)
				{
					this.content = value;
					OnPropertyChanged("Content");
				}
			}
		}
		private string content;

		public string Format
		{
			get
			{
				return this.format;
			}
			set
			{
				if (this.format != value)
				{
					this.format = value;
					OnPropertyChanged("Format");
				}
			}
		}
		private string format;

		public int? Size
		{
			get
			{
				if (this.SizeIndex <= 0)
				{
					return null;
				}
				return this.SizeIndex; //Index value is the same as size
			}
		}

		public int SizeIndex
		{
			get
			{
				return this.sizeIndex;
			}
			set
			{
				if (this.sizeIndex != value)
				{
					this.sizeIndex = value;
					OnPropertyChanged("SizeIndex");
					OnPropertyChanged("Size");
				}
			}
		}
		private int sizeIndex;

		public int? Padding
		{
			get
			{
				if (this.PaddingIndex <= 0)
				{
					return null;
				}
				return this.PaddingIndex - 1;
			}
		}

		public int PaddingIndex
		{
			get
			{
				return this.paddingIndex;
			}
			set
			{
				if (this.paddingIndex != value)
				{
					this.paddingIndex = value;
					OnPropertyChanged("PaddingIndex");
					OnPropertyChanged("Padding");
				}
			}
		}
		private int paddingIndex;

		public short? Version
		{
			get
			{
				if (this.VersionIndex <= 0)
				{
					return null;
				}
				return (short)this.VersionIndex; //Index value is the same as version
			}
		}

		public int VersionIndex
		{
			get
			{
				return this.versionIndex;
			}
			set
			{
				if (this.versionIndex != value)
				{
					this.versionIndex = value;
					OnPropertyChanged("VersionIndex");
					OnPropertyChanged("Version");
				}
			}
		}
		private int versionIndex;

		public int EncodeModeIndex
		{
			get
			{
				return this.encodeModeIndex;
			}
			set
			{
				if (this.encodeModeIndex != value)
				{
					this.encodeModeIndex = value;
					OnPropertyChanged("EncodeModeIndex");
					OnPropertyChanged("EncodeMode");
				}
			}
		}
		private int encodeModeIndex;

		public QREncodeMode EncodeMode
		{
			get
			{
				switch (this.EncodeModeIndex)
				{
					case 1:
						return QREncodeMode.Byte;

					case 2:
						return QREncodeMode.Numeric;

					case 3:
						return QREncodeMode.AlphaNumeric;
				}

				return QREncodeMode.Default;
			}
		}

		public int ErrorCorrectionIndex
		{
			get
			{
				return this.errorCorrectionIndex;
			}
			set
			{
				if (this.errorCorrectionIndex != value)
				{
					this.errorCorrectionIndex = value;
					OnPropertyChanged("ErrorCorrectionIndex");
					OnPropertyChanged("ErrorCorrection");
				}
			}
		}
		private int errorCorrectionIndex;

		public QRErrorCorrection ErrorCorrection
		{
			get
			{
				switch (this.ErrorCorrectionIndex)
				{
					case 1:
						return QRErrorCorrection.L;

					case 2:
						return QRErrorCorrection.M;

					case 3:
						return QRErrorCorrection.Q;

					case 4:
						return QRErrorCorrection.H;
				}

				return QRErrorCorrection.Default;
			}
		}

		public ImageSource ImageSource
		{
			get
			{
				return this.imageSource;
			}
			set
			{
				if (this.imageSource != value)
				{
					this.imageSource = value;
					OnPropertyChanged("ImageSource");
				}
			}
		}
		private ImageSource imageSource;

		public string PreviewUrl
		{
			get
			{
				return App.ViewModel.CreateClient().UriBuilder.BuildGenerateUrl(GetModel());
			}
		}

		public double PreviewOpacity
		{
			get
			{
				return this.IsLoading ? 0.2 : 1.0;
			}
		}

		public bool IsLoading
		{
			get
			{
				return this.isLoading;
			}
			set
			{
				if (this.isLoading != value)
				{
					this.isLoading = value;
					OnPropertyChanged("IsLoading");
					OnPropertyChanged("PreviewOpacity");
				}
			}
		}
		private bool isLoading;

		public byte[] ImageBuffer
		{
			get
			{
				return this.imageBuffer;
			}
			set
			{
				if (this.imageBuffer != value)
				{
					this.imageBuffer = value;
					OnPropertyChanged("ImageBuffer");
				}
			}
		}
		private byte[] imageBuffer;

		public ObservableCollection<ColorListItem> ColorList
		{
			get
			{
				return this.colors;
			}
			set
			{
				if (this.colors != value)
				{
					this.colors = value;
					OnPropertyChanged("ColorList");
				}
			}
		}
		private ObservableCollection<ColorListItem> colors;

		public Color? Foreground
		{
			get
			{
				if (this.ForegroundIndex > 0 && this.ForegroundIndex < this.ColorList.Count)
				{
					return this.ColorList[this.ForegroundIndex].Color;
				}
				return null;
			}
		}

		public int ForegroundIndex
		{
			get
			{
				return this.foregroundIndex;
			}
			set
			{
				if (this.foregroundIndex != value)
				{
					this.foregroundIndex = value;
					OnPropertyChanged("ForegroundIndex");
					OnPropertyChanged("Foreground");
				}
			}
		}
		private int foregroundIndex;

		public Color? Background
		{
			get
			{
				if (this.BackgroundIndex > 0 && this.BackgroundIndex < this.ColorList.Count)
				{
					return this.ColorList[this.BackgroundIndex].Color;
				}
				return null;
			}
		}

		public int BackgroundIndex
		{
			get
			{
				return this.backgroundIndex;
			}
			set
			{
				if (this.backgroundIndex != value)
				{
					this.backgroundIndex = value;
					OnPropertyChanged("BackgroundIndex");
					OnPropertyChanged("Background");
				}
			}
		}
		private int backgroundIndex;

		public string Shorten
		{
			get
			{
				switch (this.ShortenIndex)
				{
					case 1:
						return "goo.by";

					default:
						return null;
				}
			}
		}

		public int ShortenIndex
		{
			get
			{
				return this.shortenIndex;
			}
			set
			{
				if (this.shortenIndex != value)
				{
					this.shortenIndex = value;
					OnPropertyChanged("ShortenIndex");
					OnPropertyChanged("Shorten");
				}
			}
		}
		private int shortenIndex;
		#endregion

		#region Public methods
		public void Generate()
		{
			try
			{
				//Start the loading animation for better UX
				this.IsLoading = true;

				App.Logger.WriteLine("GET " + this.PreviewUrl);

				var model = GetModel();
				var client = App.ViewModel.CreateClient();
				client.GenerateCompleted += (sender, e) =>
				{
					//Stop the loading animation
					this.IsLoading = false;

					if (e.Error == null)
					{
						//Read image to buffer
						//int length = (int)e.ContentLength;
						//byte[] buffer = new byte[length];
						//e.Stream.Read(buffer, 0, length);
						//var stream = new MemoryStream(buffer);
						//this.ImageBuffer = buffer;
						//stream.Position = 0;

						//Convert buffer to image
						//var decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
						var decoder = new PngBitmapDecoder(e.Stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
						var source = decoder.Frames[0];
						this.ImageSource = source;

						App.Logger.WriteLine(string.Format("QR Code image generated. Rate limit: {0} requests remaining out of {1}", client.RateLimitRemaining, client.RateLimitTotal));
					}
					else
					{
						//Report error to the console
						App.Logger.Log(e.Error);
					}
				};
				client.GenerateAsync(model);
			}
			catch (Exception ex)
			{
				App.Logger.Log(ex);
			}
		}

		public void Save(string path)
		{
			File.WriteAllBytes(path, this.ImageBuffer);
		}

		public void SaveDialog()
		{
			MessageBox.Show("Saving to file is not available this version of demo.");
			return;

			if (this.ImageBuffer == null)
			{
				App.Logger.WriteLine("Cannot save, no QR Code generated!");
				return;
			}

			SaveFileDialog dialog = new SaveFileDialog();
			dialog.Filter = "Image (*.png)|*.png";
			dialog.DefaultExt = "*.png";

			if (dialog.ShowDialog() == true)
			{
				Save(dialog.FileName);
			}
		}

		protected QRCodeModel GetModel()
		{
			var model = new QRCodeModel()
			{
				Content = this.Content,
				Format = "png",
				Size = this.Size,
				Padding = this.Padding,
				Version = this.Version,
				EncodeMode = this.EncodeMode,
				ErrorCorrection = this.ErrorCorrection,
				Foreground = this.Foreground,
				Background = this.Background,
				Shorten = this.Shorten
			};

			return model;
		}
		#endregion

		#region INotifyPropertyChanged Members
		public event PropertyChangedEventHandler PropertyChanged;
		protected virtual void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}
		#endregion
	}
}
